
using System;
using System.IO;
using System.Collections.Generic;

namespace BumblePig
{
    public class Vec2
    {
        // member variables
        public int X;
        public int Y;

        // constructor
        public Vec2()
        {
            X = 0;
            Y = 0;
        }

        public Vec2(int nX, int nY)
        {
            X = nX;
            Y = nY;
        }

        public Vec2(Vec2 rhs)
        {
            X = rhs.X;
            Y = rhs.Y;
        }
    }

    public class StringHelper
    {
        // converts a string to an integer
        public static int StringToInteger(string strValue)
        {
            // this is the return value
            int nRetVal = 0;

            // get the length of the string
            int strLen = strValue.Length;

            // loop through all of the characters in the string
            for (int nIndex = 0; nIndex < strLen; ++nIndex)
            {
                // adjust the 10's place
                nRetVal *= 10;

                // get the next character value (Brent thinks this might be an issue)
                int nNewVal = strValue[nIndex] - '0';

                // add the new value to the return value
                nRetVal += nNewVal;
            }

            // return the newly created number!
            return nRetVal;
        }

        // this function assumes that there is only one number on the given line
        // since it uses StreamReader.ReadLine
        public static int ReadIntegerFromStream(StreamReader streamReader)
        {
            // read the new string line
            string strInput = streamReader.ReadLine();

            // return the string's integer value
            return StringHelper.StringToInteger(strInput);
        }

        // this function makes it so that there is only one number on the given line
        // since it uses the load function uses StreamReader.ReadLine
        public static void WriteIntegerToStream(StreamWriter streamWriter, int nValue)
        {
            // write the new string line
            streamWriter.WriteLine(nValue.ToString());
        }

        public static Vec2 ReadVec2FromStream(StreamReader streamReader)
        {
            // the return vector value
            Vec2 vRet = new Vec2();

            // read the x and y components from the stream
            vRet.X = ReadIntegerFromStream(streamReader);
            vRet.Y = ReadIntegerFromStream(streamReader);

            // return the stream's vector value
            return vRet;
        }

        public static void WriteVec2ToStream(StreamWriter streamWriter, Vec2 vValue)
        {
            try
            {
                // write the x and y components to the stream
                streamWriter.WriteLine(vValue.X.ToString());
                streamWriter.WriteLine(vValue.Y.ToString());
            }
            catch (Exception e)
            {
                ErrMsg(e.Data.ToString());
            }
        }

        public static void ErrMsg(string msg)
        {
            System.Windows.Forms.MessageBox.Show(msg);
        }
    }

    // stores collision box information
    public class CollisionBox
    {
        public Vec2 posMin;      // top-left position
        public Vec2 posMax;      // bottom-right position

        public CollisionBox()
        {
            posMin = new Vec2();
            posMax = new Vec2();
        }

        public bool IsCollidingWith(CollisionBox rhs)
        {
            return IsCollidingWithInternal( rhs, true );
        }

        private bool IsCollidingWithInternal(CollisionBox rhs, bool bDoubleCheck)
        {
            Vec2 posUpperR = new Vec2( rhs.posMax.X, rhs.posMin.Y );
            Vec2 posLowerL = new Vec2( rhs.posMin.X, rhs.posMax.Y );

            // need to check if any of the four corners of the rhs are contained (clock-wise check)
            if( Contains( rhs.posMin ) || Contains( posUpperR ) &&
                Contains( posLowerL ) || Contains( rhs.posMax ) )
                return true;
            
            // check to see if we want to check against the other box
            if(bDoubleCheck)
                return rhs.IsCollidingWithInternal(this, false);

            return false;
        }

        public bool Contains(Vec2 pos)
        {
            return (posMin.X <= pos.X && pos.X <= posMax.X &&   // check the X component
                    posMin.Y <= pos.Y && pos.Y <= posMax.Y);    // check the Y component
        }

        public bool Contains(int nX, int nY)
        {
            return (posMin.X <= nX && nX <= posMax.X &&   // check the X component
                    posMin.Y <= nY && nY <= posMax.Y);    // check the Y component
        }

        public void Save(StreamWriter streamWriter)
        {
            // write the top-left
            StringHelper.WriteVec2ToStream(streamWriter, posMin);

            // write the bottom-right            
            StringHelper.WriteVec2ToStream(streamWriter, posMax);
        }

        public void Load(StreamReader streamReader)
        {
            // read the top-left
            posMin = StringHelper.ReadVec2FromStream(streamReader);

            // read the bottom-right
            posMax = StringHelper.ReadVec2FromStream(streamReader);
        }
    }

    // stores the collision boxes for the frame
    public class CollisionFrame
    {
        public List<CollisionBox> m_CollisionBoxes;

        public void AddNumCollisionBoxes(int nNumBoxes)
        {
            m_CollisionBoxes = new List<CollisionBox>(nNumBoxes);

            // populate the list with the number of frames
            for (int nBoxIndex = 0; nBoxIndex < nNumBoxes; ++nBoxIndex)
            {
                m_CollisionBoxes.Add(new CollisionBox());
            }
        }

        public void AddCollisionBox(CollisionBox box)
        {
            // check to see if this has been allocated yet.
            // if it hasn't, allocate it.
            if (m_CollisionBoxes == null)
            {
                m_CollisionBoxes = new List<CollisionBox>();
            }

            // add the new box
            m_CollisionBoxes.Add(box);
        }

        public bool IsCollidingWith(CollisionFrame rhs)
        {
            // loop through our boxes and check if any of them are colliding
            // with the rhs's boxes
            int nNumBoxes = m_CollisionBoxes.Count;
            for(int nBoxIndex = 0; nBoxIndex < nNumBoxes; ++nBoxIndex)
            {
                // loop through the rhs's boxes and check collision with them
                int nRHSNumBoxes = rhs.m_CollisionBoxes.Count;
                for(int nRHSBoxIndex = 0; nRHSBoxIndex < nRHSNumBoxes; ++nRHSNumBoxes)
                {
                    // check our current box index against the rhs's current box index
                    if( m_CollisionBoxes[nBoxIndex].IsCollidingWith( rhs.m_CollisionBoxes[nRHSBoxIndex] ) )
                        return true;
                }
            }

            return false;
        }

        public void Save(StreamWriter streamWriter)
        {
            // save out how many collision boxes there are in the current frame
            int nNumBoxes = m_CollisionBoxes.Count;
            StringHelper.WriteIntegerToStream(streamWriter, nNumBoxes);

            // loop through all of the collision boxes and save them
            for (int nBoxIndex = 0; nBoxIndex < nNumBoxes; ++nBoxIndex)
            {
                m_CollisionBoxes[nBoxIndex].Save(streamWriter);
            }
        }

        public void Load(StreamReader streamReader)
        {
            // read the number of collision boxes for this frame
            int nNumBoxes = StringHelper.ReadIntegerFromStream(streamReader);

            // allocate the space for the collision box list
            AddNumCollisionBoxes(nNumBoxes);
            //m_CollisionBoxes = new List<CollisionBox>(nNumBoxes);

            // loop through all of the collision boxes and create them
            for (int nBoxIndex = 0; nBoxIndex < nNumBoxes; ++nBoxIndex)
            {
                // load the currently indexed collision box
                m_CollisionBoxes[nBoxIndex].Load(streamReader);

                //CollisionBox NewCollisionBox = new CollisionBox();

                //// load the new collision box
                //NewCollisionBox.Load(streamReader);

                //// add the newly created collision box
                //m_CollisionBoxes.Add(NewCollisionBox);
            }
        }
    }

    public class CollisionObject
    {
        // variables
        private List<CollisionFrame> m_CollisionFrames;     // list of all of the frames of collision
        private Vec2 m_posCenter;                          // center of the collision object
        private float m_fRadius;                              // radius of the collision object
        private int m_nCurFrame;                              // current frame of animation

        public CollisionObject()
        {
            m_posCenter = new Vec2(0,0);
        }

        // Get/Set functions for member variables
        public List<CollisionFrame> GetCollisionFrames()
        {
            return m_CollisionFrames;
        }

        public void AddNumCollisionFrames(int nNumFrames)
        {
            m_CollisionFrames = new List<CollisionFrame>(nNumFrames);

            // populate the list with the number of frames
            for (int nFrameIndex = 0; nFrameIndex < nNumFrames; ++nFrameIndex)
            {
                m_CollisionFrames.Add(new CollisionFrame());
            }
        }

        public void AddCollisionFrame(CollisionFrame frame)
        {
            m_CollisionFrames.Add(frame);
        }

        public Vec2 GetPosCenter() { return m_posCenter; }
        public void SetPosCenter(Vec2 pos) { m_posCenter = pos; }

        public float GetRadius() { return m_fRadius; }
        public void SetRadius(float fRadius) { m_fRadius = fRadius; }

        public int GetCurFrameIndex() { return m_nCurFrame; }
        public void SetCurFrameIndex(int nFrameIndex) { m_nCurFrame = nFrameIndex; }

        // functions
        public void Save(string strFilename, int nNumFrames)
        {
            // open the file to write the collision data
            StreamWriter streamWriter = new StreamWriter(strFilename);
            if (streamWriter == null)
            {
                System.Windows.Forms.MessageBox.Show("CollisionObject.Save - Failed to open " + strFilename + " for writing");
            }

            // write the center position
            StringHelper.WriteVec2ToStream(streamWriter, m_posCenter);

            // write the radius
            StringHelper.WriteIntegerToStream(streamWriter, (int)m_fRadius);

            // write the number of frames
            StringHelper.WriteIntegerToStream(streamWriter, nNumFrames);

            // loop through all of the frames and save them
            for (int nFrameIndex = 0; nFrameIndex < nNumFrames; ++nFrameIndex)
            {
                // save the current collision frame
                m_CollisionFrames[nFrameIndex].Save(streamWriter);
            }

            streamWriter.Flush();
            streamWriter.Close();
        }

        public bool Load(string strFilename)
        {
            // initialize the current frame to 0
            m_nCurFrame = 0;

            // open the file to read the collision data
            StreamReader streamReader = new StreamReader(strFilename);

            // read the center position
            m_posCenter = StringHelper.ReadVec2FromStream(streamReader);

            // read the radius
            m_fRadius = StringHelper.ReadIntegerFromStream(streamReader);

            // read the number of frames
            int nNumFrames = StringHelper.ReadIntegerFromStream(streamReader);

            // allocate space for collision frames list*252
            AddNumCollisionFrames(nNumFrames);
            //m_CollisionFrames = new List<CollisionFrame>(nNumFrames);
            //m_CollisionFrames = new List<CollisionFrame>();

            // loop through all of the frames and create them
            for (int nFrameIndex = 0; nFrameIndex < nNumFrames; ++nFrameIndex)
            {
                // load the currently indexed collision frame
                m_CollisionFrames[nFrameIndex].Load(streamReader);

                //// new collision frame to fill out
                //CollisionFrame NewCollisionFrame = new CollisionFrame();

                //// load the new collision frame
                //NewCollisionFrame.Load(streamReader);

                //// add the newly created collision frame
                //m_CollisionFrames.Add(NewCollisionFrame);
            }

            return true;
        }

        // returns true if the objects are colliding
        public bool IsCollidingWith(CollisionObject rhs)
        {
            // check to see if our current frame is colliding with the rhs's current frame
            return ( m_CollisionFrames[m_nCurFrame].IsCollidingWith( rhs.m_CollisionFrames[rhs.m_nCurFrame] ) );
        }
    }
}
